package puzzle;

import java.util.Calendar;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;

import aima.core.agent.Action;
import aima.core.search.framework.GraphSearch;
import aima.core.search.framework.Problem;
import aima.core.search.framework.Search;
import aima.core.search.framework.SearchAgent;
import aima.core.search.informed.AStarEvaluationFunction;
import aima.core.search.informed.AStarSearch;
import aima.core.search.informed.RecursiveBestFirstSearch;

public class PuzzleSolver {

    private static PuzzleBoard boardWith10MoveSolution = new PuzzleBoard(
            new int[] { 5, 9, 7, 6, 8, 0, 2, 10, 1, 3, 11, 4 });

    private static PuzzleBoard boardWith25MoveSolution = new PuzzleBoard(
            new int[] { 3, 1, 6, 2, 9, 0, 5, 11, 10, 8, 7, 4 });

    private static PuzzleBoard boardWith50MoveSolution = new PuzzleBoard(
            new int[] { 8, 5, 9, 10, 7, 11, 0, 3, 4, 2, 6, 1 });

    private static PuzzleBoard boardWith100MoveSolution = new PuzzleBoard(
            new int[] { 6, 9, 10, 1, 3, 2, 4, 7, 11, 5, 0, 8 });

    /**
     * @param args
     */
    public static void main(String[] args) {

        PuzzleBoard[] boards = new PuzzleBoard[] { boardWith10MoveSolution,
                boardWith25MoveSolution, boardWith50MoveSolution,
                boardWith100MoveSolution };

        for (int i = 0; i < 4; i++) {
            System.out.println("\n ---------- nuevo tablero -----------");
            System.out.println(boards[i].toString());
            System.out.println();
            puzzleAStarManhattanPlusSpecialCasesDemo(boards[i]);
            puzzleAStarManhattanDemo(boards[i]);
            puzzleRecursiveBestFirstManhattanPlusSpecialCasesDemo(boards[i]);
            puzzleRecursiveBestFirstManhattanDemo(boards[i]);
        }
    }

    private static void puzzleDemo(Search search, PuzzleBoard board) {
        try {
            Problem problem = new Problem(board,
                    PuzzleActionsFunction.getInstance(),
                    PuzzleResultFunction.getInstance(), new PuzzleGoalTest());
            long timeInMillis = Calendar.getInstance().getTimeInMillis();
            SearchAgent agent = new SearchAgent(problem, search);
            long diff = Calendar.getInstance().getTimeInMillis() - timeInMillis;
            System.out.println("\nTime in millie seconds: " + diff);
            printActions(agent.getActions());
            printInstrumentation(agent.getInstrumentation());
            printSolution(agent.getActions(), problem);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static void puzzleAStarManhattanPlusSpecialCasesDemo(
            PuzzleBoard board) {
        System.out
                .println("\nPuzzleDemo AStar Search (ManhattanPlusSpecialCasesHeuristic)-->");
        Search search = new AStarSearch(new GraphSearch(),
                new PuzzleManhattanPlusSpecialCasesHeuristicFunction());
        puzzleDemo(search, board);
    }

    private static void puzzleAStarManhattanDemo(PuzzleBoard board) {
        System.out.println("\nPuzzleDemo AStar Search (ManhattanHeursitic)-->");
        Search search = new AStarSearch(new GraphSearch(),
                new PuzzleManhattanHeuristicFunction());
        puzzleDemo(search, board);
    }

    private static void puzzleRecursiveBestFirstManhattanPlusSpecialCasesDemo(
            PuzzleBoard board) {
        System.out
                .println("\nPuzzleDemo Recursive Best First (ManhattanPlusSpecialCasesHeuristic)-->");
        Search search = new RecursiveBestFirstSearch(
                new AStarEvaluationFunction(
                        new PuzzleManhattanPlusSpecialCasesHeuristicFunction()));
        puzzleDemo(search, board);
    }

    private static void puzzleRecursiveBestFirstManhattanDemo(PuzzleBoard board) {
        System.out
                .println("\nPuzzleDemo Recursive Best First (ManhattanHeursitic)-->");
        Search search = new RecursiveBestFirstSearch(
                new AStarEvaluationFunction(
                        new PuzzleManhattanHeuristicFunction()));
        puzzleDemo(search, board);
    }

    private static void printInstrumentation(Properties properties) {
        Iterator<Object> keys = properties.keySet().iterator();
        while (keys.hasNext()) {
            String key = (String) keys.next();
            String property = properties.getProperty(key);
            System.out.println(key + " : " + property);
        }
    }

    private static void printActions(List<Action> actions) {
        for (int i = 0; i < actions.size(); i++) {
            String action = actions.get(i).toString();
            System.out.println(action);
        }
    }

    private static void printSolution(List<Action> actions, Problem problem) {
        PuzzleBoard originalBoard = (PuzzleBoard) problem.getInitialState();
        PuzzleBoard solutionBoard = new PuzzleBoard(originalBoard.getBoard()
                .clone());
        for (int i = 0; i < actions.size(); i++) {
            solutionBoard = (PuzzleBoard) problem.getResultFunction().result(
                    solutionBoard, actions.get(i));
        }
        System.out.println(solutionBoard.toString());
    }

}

